﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Grpc.Core;
using MX.Core.Extensions;
using MX.Core.Log;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UserCenter.Api.Core;
using UserCenter.Api.Plugins;
using UserCenter.Api.Tools;
using UserCenter.BLL;
using UserCenter.Grpc;
using UserCenter.Grpc.Client;
using UserCenter.Models;

namespace UserCenter.Api.Services
{
    public class MerchantApiImpl : MerchantApi.MerchantApiBase
    {
        const int ECH = 0x7;
        BaseMerchantImpl _baseMerchantImpl = new BaseMerchantImpl();
        WeChatClient weChatClient = new WeChatClient();
        /// <summary>
        /// 密码验证
        /// </summary>
        /// <param name="request"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Task<AuthResponse> PwdAuth(PwdAuthRequest request, ServerCallContext context)
        {
            return _baseMerchantImpl.PwdAuth(request, context);
        }

        public override Task<RegisterResponse> PwdRegister(PwdRegisterRequest request, ServerCallContext context)
        {
            return _baseMerchantImpl.PwdRegister(request, context);
        }


        public override Task<CheckTokenResponse> CheckToken(CheckTokenRequest request, ServerCallContext context)
        {
            return _baseMerchantImpl.CheckToken(request, context);
        }

        public override Task<GetMemberResponse> GetMember(GetMemberRequest request, ServerCallContext context)
        {
            return _baseMerchantImpl.GetMember(request, context);
        }

        public override Task<SendSmsResponse> SendSms(SendSmsRequest request, ServerCallContext context)
        {
            bool checkSms(List<SmsCodeLog> list, Func<SmsCodeLog, bool> predicate, int time, out int remainSeconds)
            {
                remainSeconds = 0;
                var lastLog = list.
                     Where(predicate).
                     OrderByDescending(l => l.CreateTime).
                     Take(1).SingleOrDefault();
                if (lastLog != null)
                {
                    var span = lastLog.CreateTime.AddSeconds(time) - DateTime.Now;

                    if (span.TotalSeconds > 0)
                    {
                        remainSeconds = Convert.ToInt32(span.TotalSeconds);
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
                else
                {
                    return true;
                }
            }
            string ip = context.ClientIp();
            SendSmsResponse smsResponse = new SendSmsResponse();
            smsResponse.Success = false;
            SmsType? smsType;
            if (string.IsNullOrEmpty(request.DeviceId.Trim()))
            {
                smsResponse.Message = "设备Id错误";
            }
            else if (string.IsNullOrEmpty(request.Phone.Trim()))
            {
                smsResponse.Message = "手机号码错误";
            }
            else if (!Utils.CheckByteEnum(request.Type, out smsType))
            {
                smsResponse.Message = "短信类型错误";
            }
            else if (smsType.Value == SmsType.Register)
            {
                bool exists = MemberBLL.CheckUserName(request.Phone);
                if (exists)
                {
                    context.Throw(ECH, 16);
                    return null;
                }
            }
            else
            {
                var logs = SmsCodeLogBLL.List(ip, request.DeviceId, request.Phone);
                if (!checkSms(logs, (l) => l.DeviceId.Equals(request.DeviceId), 120, out int remain))
                {
                    smsResponse.Message = "每个设备两分钟内最多发送一条";
                    smsResponse.RemainSeconds = remain;
                }
                else if (!checkSms(logs, (l) => l.Phone.Equals(request.Phone), 120, out remain))
                {
                    smsResponse.Message = "每个手机号码两分钟内最多发送一条";
                    smsResponse.RemainSeconds = remain;
                }
                else if (!checkSms(logs, (l) => l.IpAddress.Equals(ip), 60, out remain))
                {
                    smsResponse.Message = "每个IP一分钟内最多发送一条";
                    smsResponse.RemainSeconds = remain;
                }
                else
                {
                    string smsId = Guid.NewGuid().ToString("N");
                    Random random = new Random(smsId.GetHashCode());
                    string code = random.Next(1000, 9999).ToString();
                    var sendResult = SmsFactory.Install.Send(smsType.Value, request.Phone, code);
                    if (!sendResult.Success)
                    {
                        if (string.IsNullOrEmpty(sendResult.Message))
                        {
                            smsResponse.Message = "短信网关返回失败";
                        }
                        else
                        {
                            smsResponse.Message = sendResult.Message;
                        }
                    }
                    else
                    {
                        bool save = SmsCodeLogBLL.Save(new SmsCodeLog()
                        {
                            Code = code,
                            CreateTime = DateTime.Now,
                            DeviceId = request.DeviceId,
                            IpAddress = ip,
                            IsValidate = false,
                            OutSendId = sendResult.QueryId,
                            Phone = request.Phone,
                            SmsId = smsId,
                            Type = smsType.Value
                        });
                        if (!save)
                        {
                            smsResponse.Message = "短信结果存储失败";
                        }
                        else
                        {
                            smsResponse.SmsId = smsId;
                            smsResponse.Success = true;
                        }
                    }


                }
            }

            return Task.FromResult(smsResponse);
        }


        public override Task<AuthResponse> SmsAuth(SmsAuthRequest request, ServerCallContext context)
        {
            DeviceType? deviceType = null;
            var log = SmsCodeLogBLL.Info(request.SmsId);
            if (log == null)
            {
                context.Throw(ECH, 17);
                return null;
            }
            if (log.IsValidate || log.CreateTime.AddMinutes(5) < DateTime.Now)
            {
                context.Throw(ECH, 18);
                return null;
            }
            if (!Utils.CheckByteEnum(request.DeviceType, out deviceType))
            {
                context.Throw(ECH, 18);
                return null;
            }
            string code = request.Code;
            var accounts = string.IsNullOrEmpty(ApiConfig.Default.TestAccount) ? new string[] { } : ApiConfig.Default.TestAccount.Split(',');
            if (accounts.Contains(log.Phone))
            {
                code = log.Code;
            }
            if (!log.Code.Equals(code, StringComparison.InvariantCultureIgnoreCase))
            {
                context.Throw(ECH, 19);
                return null;
            }
            else
            {
                string ip = context.ClientIp();
                long memberId = 0;
                Member.Status loginStatus = Member.Status.Disable;
                var member = MemberBLL.Info(context.MerchantId(), log.Phone);
                if (member != null)
                {
                    loginStatus = member.LoginStatus;
                    memberId = member.MemberId;
                }
                else
                {
                    memberId = MemberBLL.Create(context.MerchantId(), log.Phone, ApiConfig.Default.DefaultAvaterUrl);
                    if (memberId < 1)
                    {
                        context.Throw(ECH, 05);
                        return null;
                    }
                    else
                    {
                        loginStatus = Member.Status.Enable;
                    }
                }
                if (loginStatus == Member.Status.Enable)
                {
                    string token = Guid.NewGuid().ToString("N");
                    if (MemberBLL.Auth(memberId,
                        token, ip,
                        log.DeviceId,
                        request.DeviceName,
                        deviceType.Value,
                        AuthSource.Client))
                    {
                        //TODO 验证码设为不可用
                        return Task.FromResult(new AuthResponse
                        {
                            Status = AuthStatus.Success,
                            Expire = DateTime.Now.AddDays(7).ToTimestamp(),
                            MemberId = memberId,
                            Token = token
                        });
                    }
                    else
                    {
                        return Task.FromResult(new AuthResponse
                        {
                            Status = AuthStatus.Faild
                        });
                    }
                }
                else
                {
                    return Task.FromResult(new AuthResponse
                    {
                        Status = AuthStatus.MemberStatusErr
                    });
                }
            }

        }

        public override Task<RegisterResponse> SmsRegister(SmsRegisterRequest request, ServerCallContext context)
        {
            var log = SmsCodeLogBLL.Info(request.SmsId);
            if (log != null)
            {
                context.Throw(ECH, 17);
                return null;
            }
            if (log.IsValidate || log.CreateTime.AddMinutes(5) < DateTime.Now)
            {
                context.Throw(ECH, 18);
                return null;
            }
            if (!log.Code.Equals(request.Code, StringComparison.InvariantCultureIgnoreCase))
            {
                context.Throw(ECH, 19);
                return null;
            }
            if (MemberBLL.CheckUserName(log.Phone))
            {
                context.Throw(ECH, 16);
                return null;
            }
            else
            {
                var memberId = MemberBLL.Create(context.MerchantId(), log.Phone, ApiConfig.Default.DefaultAvaterUrl);
                if (memberId < 1)
                {
                    context.Throw(ECH, 05);
                    return null;
                }
                else
                {
                    return Task.FromResult(new RegisterResponse()
                    {
                        MemberId = memberId
                    });
                }
            }
        }
        public override Task<BoolValue> CheckParent(MemberIdRequest request, ServerCallContext context)
        {
            BoolValue boolValue = new BoolValue();
            boolValue.Value = MemberBLL.GetParentId(request.MemberId) > 0;
            return Task.FromResult(boolValue);
        }


        public override Task<GetMemberResponse> GetMemberByUserName(GetMemberByUserNameRequest request, ServerCallContext context)
        {
            var member = MemberBLL.Info(context.MerchantId(), request.UserName);
            return _baseMerchantImpl.GetMember(member, context);
        }


        public override Task<AuthResponse> WeChatAuth(WeChatAuthRequest request, ServerCallContext context)
        {
            if (!Utils.CheckByteEnum(request.DeviceType, out DeviceType? deviceType))
            {
                context.Throw(ECH, 18);
                return null;
            }
            var jsonObj = JsonConvert.DeserializeObject(request.Json) as JObject;
            Func<string, string> checkVal = (string key) =>
            {
                if (jsonObj.ContainsKey(key))
                {
                    return jsonObj[key].ToString();
                }
                return "";
            };
            if (jsonObj.ContainsKey("unionid"))
            {
                var unionId = jsonObj["unionid"].ToString();
                var info = WxLoginInfoBLL.Info(unionId);
                if (info == null)
                {
                    info = new WxLoginInfo()
                    {
                        Unionid = checkVal("unionid"),
                        Country = checkVal("country"),
                        AppId = checkVal("appid"),
                        City = checkVal("city"),
                        HeadImgUrl = checkVal("icon"),
                        NickName = checkVal("nickname"),
                        OpenId = checkVal("openid"),
                        Privilege = checkVal("privilege"),
                        Province = checkVal("province"),
                        Sex = checkVal("gender"),
                        CreateTime = DateTime.Now,
                    };
                    if (info.HeadImgUrl.EndsWith("/132"))
                    {
                        info.HeadImgUrl = info.HeadImgUrl.Substring(0, info.HeadImgUrl.Length - 3) + "0";
                    }
                    string avaterUrl = null;
                    try
                    {
                        var data = weChatClient.DownloadIco(info.HeadImgUrl);
                        Task.Run(() =>
                        {
                            avaterUrl = Utils.UploadBase64Image(context, data, true);
                        }).Wait(timeout: TimeSpan.FromSeconds(2));
                    }
                    catch (Exception ex)
                    {
                        Logger.Default.Warn(ex, "微信登录头像数据处理失败");
                    }
                    if (string.IsNullOrEmpty(avaterUrl))
                    {
                        avaterUrl = ApiConfig.Default.DefaultAvaterUrl;
                    }
                    var userName = $"wx_{info.OpenId.MD5().Substring(8, 16)}";
                    //{"nickName":"华商情","email":"13396196332@163.com","gender":"男","birthday":"2009-09-04","areaId":14887}
                    var memberId = MemberBLL.Create(context.MerchantId(), userName, avaterUrl);
                    if (memberId > 0)
                    {
                        info.MemberId = memberId;
                        try
                        {
                            Dictionary<string, string> extObject = new Dictionary<string, string>();
                            extObject["nickName"] = info.NickName;
                            extObject["gender"] = info.Sex == "0" ? "男" : "女";
                            MemberBLL.SetExt(memberId, JsonConvert.SerializeObject(extObject, Formatting.None), false);
                        }
                        catch (Exception ex)
                        {
                            Logger.Default.Warn(ex, "微信登录扩展数据处理失败");
                        }
                        try
                        {
                            WxLoginInfoBLL.Insert(info);
                        }
                        catch (Exception ex)
                        {
                            Logger.Default.Error(ex, "微信登录写入登录数据失败");
                            context.Throw(ECH, 28);
                            return null;
                        }
                    }
                    else
                    {
                        context.Throw(ECH, 05);
                        return null;
                    }

                }
                if (info != null)
                {
                    var member = MemberBLL.Info(info.MemberId);
                    //TODO 授权前需坚持合法性
                    if (member != null)
                    {
                        string ip = context.ClientIp();
                        string token = Guid.NewGuid().ToString("N");
                        if (MemberBLL.Auth(member.MemberId,
                            token, ip,
                            request.DeviceId,
                            request.DeviceName,
                            deviceType.Value,
                            AuthSource.Wechat))
                        {
                            return Task.FromResult(new AuthResponse
                            {
                                Status = AuthStatus.Success,
                                Expire = DateTime.Now.AddDays(7).ToTimestamp(),
                                MemberId = member.MemberId,
                                Token = token
                            });
                        }
                        else
                        {
                            return Task.FromResult(new AuthResponse
                            {
                                Status = AuthStatus.Faild
                            });
                        }
                    }
                    else
                    {
                        context.Throw(ECH, 27);
                        return null;
                    }

                }
                else
                {
                    context.Throw(ECH, 26);
                    return null;
                }
            }
            else
            {
                context.Throw(ECH, 26);
                return null;
            }

        }

        public override Task<GetGroupResponse> GetGroupByToken(GetGroupByTokenRequest request, ServerCallContext context)
        {
            var tokens = MemberBLL.TokenList(request.Tokens.ToList());
            var memberIds = tokens.Select(s => s.MemberId).ToList();
            var members = MemberBLL.InfoList(memberIds);
            var layerCounts = MemberBLL.LayerCountList(memberIds);
            var groupList = members.Select(member =>
            {
                var layerCount = layerCounts.Where(s => s.MemberId == member.MemberId).FirstOrDefault();
                return new MemberGroupInfoMessage()
                {
                    AvatarUrl = member.AvatarUrl,
                    MemberId = member.MemberId,
                    ChildCount = layerCount == null ? 0 : layerCount.ChildCount,
                    GroupCount = layerCount == null ? 0 : layerCount.GroupCount,
                    UserName = member.UserName
                };
            });
            var reponse = new GetGroupResponse();
            reponse.Groups.Add(groupList);
            return Task.FromResult(reponse);
        }


        public override Task<BoolValue> CombinAccount(CombinAccountRequest request, ServerCallContext context)
        {
            var mainToken = MemberBLL.Token(request.MainToken);
            var combinTokens = MemberBLL.TokenList(request.CombinTokens.ToList());
            if (mainToken == null)
            {
                context.Throw(ECH, 31);
                return null;
            }
            var mainMemberId = mainToken.MemberId;
            var combinMemberIds = combinTokens.Select(s => s.MemberId).ToList();
            if (combinMemberIds.Count < 1)
            {
                context.Throw(ECH, 30);
                return null;
            }
            var r = MemberBLL.CombinAccount(request.UserName, mainMemberId, combinMemberIds);
            return Task.FromResult(new BoolValue() { Value = r });
        }
    }
}
